﻿using HCLSMod;
using HCLSMod.NamePipe;
using LModBusRTU;
using LNamePipe;
using PLCNodeInterfaceBase;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Timers;
using System.Windows.Forms;
using ZLSerialPortBusLib.SerialPortBus;

namespace ZLSerialPortBusLib.PLCManager
{
    #region 序列化的对象
    [Serializable]
    public class ZLPLCNodeInfo
    {
        public DateTime DataTimeTag = DateTime.Now;
        /// <summary>
        /// PLC地址，，如果是RTU数据，仅PLCAddress0，
        /// 如果是TCPIP数据，对应IP地址4位，PLCAddress0对应最低位
        /// </summary>
        public byte PLCAddress0 = 0;
        public byte PLCAddress1 = 0;
        public byte PLCAddress2 = 0;
        public byte PLCAddress3 = 0;
        /// <summary>
        /// PLC寄存器起始地址，默认为0
        /// </summary>
        public ushort RegAddrStar = 0;
        /// <summary>
        /// PLC寄存器数据
        /// </summary>
        public ushort[] RegValues = new ushort[0];
    }
    #endregion

    public delegate bool ModbusNodeWriteRegBack(object sender, TModbusRTUCommandV2 WriteRegCommand);

    /// <summary>
    /// PLC基础类，主要存放PLC寄存器映射，生成基础的Modbus命令，
    /// 通过命令的回调事件，处理应答的数据
    /// 修改：2018-07-02
    /// 1、增加接口PLCNodeInterface
    /// 2、增加寄存器写回方式2
    /// 说明：
    /// 1、该模块2种方式写回寄存器
    /// A、WriteRegValue（写回1个寄存器），
    /// 将需要发送的命令放入中间属性TModbusRTUCommandV2 WriteRegCommand，
    /// 由上级总线自动判断该函数是否为空，不为空则发送数据
    /// B、WriteRegValue（写回多个寄存器）利用事件ModbusNodeWriteRegBack OnWriteRegBacke，将生成的命令发送；
    /// </summary>
    public class ZLPLCModbusNode : PLCNodeInterface
    {
        public event TMBCommandResponCallBack OnGetRespon;

        public event ModbusNodeWriteRegBack OnWriteRegBacke;

        #region 接口：PLCNodeInterface

        public PLCNodeProtocolType PLCProtocolType { get => PLCNodeProtocolType.Modbus; }
        /// <summary>
        /// PLC是否正常连接，可重载
        /// </summary>
        public virtual bool IsConnted { get { return PLCDisconnectCount < 5; }  }
        /// <summary>
        /// PLC寄存器数量
        /// </summary>
        public virtual int PLCRegCount { get { return fRegisters.Count; } }
        public virtual int PLCAddress0 { get { return fPLCIPAddress[0]; } }
        /// <summary>
        /// 节点对应的PLC网络地址
        /// </summary>
        protected IPEndPoint fNodeIPEndPoint { get; set; }
        public virtual IPEndPoint PLCIPEndPoint { get { return fNodeIPEndPoint; } }
        public virtual int PLCRegAddrStar { get { return fRegStar; } }

        /// <summary>
        /// 刷新数据周期，单位：ms，最小周期30ms
        /// </summary>
        public int IntervalOfFlash
        {
            get { return fIntervalOfFlash; }
            set { fIntervalOfFlash = (value < 10) ? 10 : value; }
        }
        /// <summary>
        /// 数据是否已经刷新，读取后自动变成false
        /// </summary>
        public bool HasNewData
        {
            get
            {
                bool fValue = fHasNewData;
                fHasNewData = false;
                return fValue;
            }
        }

        public virtual bool ReadRegValueAbsAddr(int fAbsRegAddress, out ushort fRegValue)
        {
            return ReadRegValue(fAbsRegAddress, out fRegValue);
        }

        public virtual bool WriteRegValue(ushort fRegStar, ushort[] fRegValues)
        {
            if (((fRegStar - this.RegStar) < fRegisters.Count) &&
                (fRegStar >= this.RegStar) &&
                (fRegStar + fRegValues.Length < this.RegStar + this.RegCount))
            {
                TModbusRTUCommandV2 NewWriteRegCommand = MBCommand_WriteRegValue(fRegStar, fRegValues);
                if (OnWriteRegBacke != null)
                    return OnWriteRegBacke(this, NewWriteRegCommand);
            }
            return false;
        }

        /// <summary>
        /// 等待执行的写入寄存器命令
        /// </summary>
        public TModbusRTUCommandV2 WriteRegCommand { get; set; }

        public virtual bool WriteRegValue(int fRegAddress, ushort fRegValue)
        {
            if (WriteRegCommand == null)
            {
                if (((fRegAddress - this.RegStar) < fRegisters.Count) && (fRegAddress >= this.RegStar) && (fRegAddress < this.RegStar + this.RegCount)
                    && ((fRegAddress - this.RegStar) >= 0))
                {
                    try
                    {
                        ushort[] fValues = { fRegValue };
                        WriteRegCommand = MBCommand_ReadWriteRegValue(fRegAddress, ref fValues);
                    }
                    catch
                    {

                    }
                    return true;
                }
            }
            return false;
        }

        public virtual void CloseAll()
        { }

        /// <summary>
        /// 全部寄存器的数据
        /// </summary>
        public ushort[] RegValues
        {
            get
            {
                MutexRegRW.WaitOne();
                try
                {
                    return fRegisters.ToArray();
                }
                finally
                {
                    MutexRegRW.ReleaseMutex();
                }
            }
        }

        public bool SendMBCommand(object MBCommand)
        {
            if (OnWriteRegBacke != null)
                return OnWriteRegBacke(this, (TModbusRTUCommandV2)MBCommand);
            else
                return false;
        }
        #endregion

        protected string sWarningInfo = "";

        // 是否是读取保持寄存器；true：读取保持寄存器，使用命令0x03；false：读取输入寄存器，使用命令0x04
        // 增加：2019-10-19
        public bool ReadMemoryReg { get; set; }

        private List<ushort> fRegisters { get; set; }

        private Mutex MutexRegRW = new Mutex(false, "MutexRegisterReadWrite:" + Guid.NewGuid().ToString());

        /// <summary>
        /// 全部写入寄存映象
        /// </summary>
        /// <param name="RegValues"></param>
        /// <returns></returns>
        protected int SetRegisters(ushort[] RegValues)
        {
            MutexRegRW.WaitOne();
            try
            {
                fRegisters = new List<ushort>(RegValues);
                return fRegisters.Count;
            }
            finally
            {
                MutexRegRW.ReleaseMutex();
            }
        }
        /// <summary>
        /// 写入1个寄存映象
        /// </summary>
        /// <param name="RegAddrDef">寄存器相对地址，从0开始</param>
        /// <param name="RegValue"></param>
        /// <returns></returns>
        protected int SetRegister(ushort RegAddrDef, ushort RegValue)
        {
            MutexRegRW.WaitOne();
            try
            {
                if (RegAddrDef < fRegisters.Count)
                {
                    fRegisters[RegAddrDef] = RegValue;
                    return fRegisters.Count;
                }
                return -1;
            }
            finally
            {
                MutexRegRW.ReleaseMutex();
            }
        }

        /// <summary>
        /// 寄存器数量
        /// </summary>
        public int RegCount { get { return fRegisters.Count; } }

        protected byte[] fPLCIPAddress = new byte[4];

        /// <summary>
        /// Modbus RTU地址
        /// </summary>
        public int PLCMBAddress { get { return fPLCIPAddress[0]; } }
        /// <summary>
        /// TCPIP地址0
        /// </summary>
        public int PLCIPAddress0 { get { return fPLCIPAddress[0]; } }
        public int PLCIPAddress1 { get { return fPLCIPAddress[1]; } }
        public int PLCIPAddress2 { get { return fPLCIPAddress[2]; } }
        public int PLCIPAddress3 { get { return fPLCIPAddress[3]; } }

        protected ushort fRegStar = 0;
        /// <summary>
        /// 寄存器起始地址
        /// </summary>
        public int RegStar { get { return fRegStar; } }
        /// <summary>
        /// PLC丢失连接累计
        /// </summary>
        protected int fPLCDisonnectCount = 0;
        /// <summary>
        /// PLC丢失连接累计
        /// </summary>
        public int PLCDisconnectCount { get { return fPLCDisonnectCount; } }

        protected bool fHasNewData = false;
        
        /// <summary>
        /// 配置信息字符串
        /// </summary>
        /// <returns></returns>
        public string CFGStr
        {
            get
            {
                return string.Format("{0},{1},{2},{3}",
                    fPLCIPAddress[0].ToString(),
                    fRegStar.ToString(),
                    fRegisters.Count.ToString(),
                    fIntervalOfFlash.ToString());
            }
        }
        /// <summary>
        /// 节点最后访问时间
        /// </summary>
        public DateTime LastComunication { get; set; }

        protected int fIntervalOfFlash = 10;


        public string PLCRunInfo { get { return CFGStr + "," + fPLCDisonnectCount.ToString(); } }


        public ZLPLCModbusNode()
        {
            ReadMemoryReg = true;
            fPLCIPAddress[0] = 0;
            fPLCIPAddress[1] = 0;
            fPLCIPAddress[2] = 0;
            fPLCIPAddress[3] = 0;
            fRegStar = 0;
            SetRegisters(new ushort[0x20]);
            LastComunication = DateTime.Now;
        }

        public ZLPLCModbusNode(ZLPLCNodeInfo fSerialPLCNode) : this()
        {
            if (fSerialPLCNode != null)
            {
                fPLCIPAddress[0] = fSerialPLCNode.PLCAddress0;
                fRegStar = fSerialPLCNode.RegAddrStar;
                SetRegisters(fSerialPLCNode.RegValues);
            }
        }

        public ZLPLCModbusNode(int fPLCAddress) : this()
        {
            fPLCIPAddress[0] = (byte)fPLCAddress;
        }

        public ZLPLCModbusNode(int fPLCAddress, int fPLCRegStar, int fPLCRegCount) : this()
        {
            fPLCIPAddress[0] = (byte)fPLCAddress;
            fRegStar = (ushort)fPLCRegStar;            
            SetRegisters(new ushort[fPLCRegCount]);
        }
        /// <summary>
        /// 根据格式化的字符串创建PLC节点，与CFGStr相同结构
        /// 格式：A,B,C    
        /// A:Modus地址；
        /// B:寄存器起始地址；
        /// C:寄存器数量；
        /// </summary>
        /// <param name="fPLCInfo">格式化的字符串</param>
        public ZLPLCModbusNode(string fPLCInfo) : this()
        {
            if (fPLCInfo.IndexOf(',') > 0)
            {
                int fRegCount = 0;
                string[] fPLCInfoList = fPLCInfo.Split(',');
                if ((fPLCInfoList.Length >= 4)
                    && byte.TryParse(fPLCInfoList[0], out fPLCIPAddress[0])
                    && ushort.TryParse(fPLCInfoList[1], out fRegStar)
                    && int.TryParse(fPLCInfoList[2], out fRegCount)
                    && int.TryParse(fPLCInfoList[3], out fIntervalOfFlash)
                    && (fIntervalOfFlash >= 10)
                    && (fRegCount > 0))
                {
                    SetRegisters(new ushort[fRegCount]);
                }
                else
                    fPLCIPAddress[0] = 0;
            }
        }

        public void SetPLCAddress(byte iPLCAddress)
        {
            if (iPLCAddress > 0)
                fPLCIPAddress[0] = iPLCAddress;
        }
        /// <summary>
        /// 读取PLC寄存器的值
        /// </summary>
        /// <param name="fRegAddress">寄存器地址，使用绝对地址，从0开始</param>
        /// <param name="fRegValue">返回读取的值</param>
        /// <returns>是否成功读取，失败主要是地址错误</returns>
        public bool ReadRegValue(int fRegAddress, out ushort fRegValue)
        {
            fRegValue = 0;
            if (((fRegAddress - this.RegStar) < fRegisters.Count) && (fRegAddress >= this.RegStar) && (fRegAddress < this.RegStar + this.RegCount)
                && ((fRegAddress - this.RegStar) >= 0))
            {
                MutexRegRW.WaitOne();
                try
                {
                    fRegValue = fRegisters[fRegAddress - this.RegStar];
                }
                finally
                {
                    MutexRegRW.ReleaseMutex();
                }
                return true;
            }
            return false;
        }
        

        public int PLCLostRespon(int fCount = 0)
        {
            fPLCDisonnectCount += fCount;
            return fPLCDisonnectCount;
        }

        public void ResetPLCConnect()
        {
            fPLCDisonnectCount = 0;
        }

        /// <summary>
        /// 询问PLC地址
        /// </summary>
        /// <returns></returns>
        public TModbusRTUCommandV2 MBCommand_AskPLCAddr()
        {            
            TModbusRTUCommandV2 fMBCommand = new TModbusRTUCommandV2(ModbusCommand.ReadMemoryReg_0x03);
            fMBCommand.PLCAddress = 0;
            fMBCommand.RegReadAddress = 0;
            fMBCommand.RegReadCount = 1;
            fMBCommand.OnGetRTURespon += MBCommand_OnGetRespon;
            return fMBCommand;
        }
        /// <summary>
        /// 读取PLC全部寄存器地址
        /// </summary>
        /// <returns></returns>
        public TModbusRTUCommandV2 MBCommand_ReadRegValue()
        {
            
            TModbusRTUCommandV2 fMBCommand = new TModbusRTUCommandV2(ModbusCommand.ReadMemoryReg_0x03);
            if (ReadMemoryReg)
                fMBCommand = new TModbusRTUCommandV2(ModbusCommand.ReadMemoryReg_0x03);
            else
                fMBCommand = new TModbusRTUCommandV2(ModbusCommand.ReadInputReg_0x04);
            fMBCommand.PLCAddress = (byte)PLCMBAddress;
            fMBCommand.RegReadAddress = (ushort)RegStar;
            fMBCommand.RegReadCount = (ushort)RegCount;
            fMBCommand.OnGetRTURespon += MBCommand_OnGetRespon;
            return fMBCommand;
        }

        /// <summary>
        /// 写入数据后，再读取PLC全部寄存器地址
        /// </summary>
        public TModbusRTUCommandV2 MBCommand_ReadWriteRegValue(int fStarReg, ref ushort[] fRegValue)
        {
            TModbusRTUCommandV2 fMBCommand = new TModbusRTUCommandV2(ModbusCommand.RWMemoryReg_0x17);
            fMBCommand.PLCAddress = (byte)PLCMBAddress;
            fMBCommand.RegReadAddress = (ushort)RegStar;
            fMBCommand.RegReadCount = (ushort)RegCount;
            fMBCommand.RegWriteAddress = (ushort)fStarReg;
            fMBCommand.RegWriteCount = (ushort)fRegValue.Length;

            for (int i = 0; i < fRegValue.Length; i++)
                fMBCommand.RegValue[i] = (ushort)fRegValue[i];
            fMBCommand.OnGetRTURespon += MBCommand_OnGetRespon;
            return fMBCommand;
        }

        /// <summary>
        /// 生成写入寄存器值的命令
        /// </summary>
        /// <param name="fRegStar">要写入的起始地址</param>
        /// <param name="fRegDatas">要写入的数据</param>
        /// <returns>生成的命令</returns>
        public TModbusRTUCommandV2 MBCommand_WriteRegValue(int fRegStar, ushort[] fRegValue)
        {
            if ((fRegValue != null) && (fRegValue.Length > 0))
            {
                TModbusRTUCommandV2 fCommand = new TModbusRTUCommandV2(ModbusCommand.WriteMemoryReg_0x10);
                fCommand.PLCAddress = (byte)PLCMBAddress;
                fCommand.RegWriteAddress = (ushort)fRegStar;
                fCommand.RegReadCount = (ushort)fRegValue.Length;
                fCommand.RegValue = fRegValue;
                return fCommand;
            }
            return null;
        }

        private void MBCommand_OnGetRespon(object sender)
        {
            TModbusRTUCommandV2 fMBCommand = (TModbusRTUCommandV2)sender;
            if (fMBCommand.HasReply)
            {
                if ((fMBCommand.PLCAddress == 0) && (PLCMBAddress == 0) && (fMBCommand.ReplyValues.Length > 0))
                    fPLCIPAddress[0] = (byte)fMBCommand.ReplyValues[0];
                if ((PLCMBAddress != 0) && (fMBCommand.PLCAddress == PLCMBAddress))
                {
                    // 读取命令，将寄存器数据写入本机
                    if (((fMBCommand.Command == ModbusCommand.ReadMemoryReg_0x03) ||
                        (fMBCommand.Command == ModbusCommand.ReadInputReg_0x04) ||
                           (fMBCommand.Command == ModbusCommand.RWMemoryReg_0x17))
                           && (fMBCommand.ReplyValues.Length == this.RegCount))
                    {
                        SetRegisters(fMBCommand.ReplyValues);
                        fHasNewData = true;
                    }
                    fPLCDisonnectCount = 0;
                    if (OnGetRespon != null)
                        OnGetRespon(this);
                }
            }
            else
                fPLCDisonnectCount++;
        }

        /// <summary>
        /// 实现接口，向外提供PLC节点信息
        /// </summary>
        /// <returns></returns>
        public virtual ZLPLCNodeInfo GetSerializedObjectOfPLCInfo()
        {
            ZLPLCNodeInfo fResult = new ZLPLCNodeInfo();
            fResult.PLCAddress0 = (byte)PLCMBAddress;
            fResult.RegAddrStar = (ushort)RegStar;
            fResult.RegValues = RegValues;
            return fResult;
        }

        public virtual byte[] GetSerializedDataOfPLCInfo()
        {
            return BaseLib.SerializeObject(GetSerializedObjectOfPLCInfo());
        }

        /// <summary>
        /// PLC序列号
        /// </summary>
        public string PLCSNStr
        {
            get
            {
                MutexRegRW.WaitOne();
                try
                {
                    if (fRegisters.Count >= 0x10)
                        return string.Format("{0:X2}{1:X2}{2:X2}{3:X2}{4:X2}{5:X2}{6:X2}{7:X2}{8:X2}{9:X2}{10:X2}{11:X2}",
                            (fRegisters[0x02 + 0] & 0xFF),      // 寄存器中低位在前
                            ((fRegisters[0x02 + 0] >> 8) & 0xFF), // 寄存器中高位在后
                            (fRegisters[0x02 + 1] & 0xFF),
                            ((fRegisters[0x02 + 1] >> 8) & 0xFF),
                            (fRegisters[0x02 + 2] & 0xFF),
                            ((fRegisters[0x02 + 2] >> 8) & 0xFF),
                            (fRegisters[0x02 + 3] & 0xFF),
                            ((fRegisters[0x02 + 3] >> 8) & 0xFF),
                            (fRegisters[0x02 + 4] & 0xFF),
                            ((fRegisters[0x02 + 4] >> 8) & 0xFF),
                            (fRegisters[0x02 + 5] & 0xFF),
                            ((fRegisters[0x02 + 5] >> 8) & 0xFF));
                    return "";
                }
                finally
                {
                    MutexRegRW.ReleaseMutex();
                }
            }
        }

        /// <summary>
        /// PLC序列号
        /// </summary>
        public byte[] PLCSNByte
        {
            get
            {
                byte[] fValue = new byte[12];
                if (!string.IsNullOrEmpty(PLCSNStr))
                {
                    MutexRegRW.WaitOne();
                    try
                    {
                        for (int i = 0; i < 6; i++)
                        {
                            fValue[i * 2] = (byte)(fRegisters[0x02 + i] & 0xFF);
                            fValue[i * 2 + 1] = (byte)((fRegisters[0x02 + i] >> 8) & 0xFF);
                        }
                    }
                    finally
                    {
                        MutexRegRW.ReleaseMutex();
                    }
                    return fValue;
                }
                return null;
            }
        }
        /// <summary>
        /// 低位在前的PLC序列号
        /// </summary>
        public byte[] PLCSNByteLF
        {
            get
            {
                byte[] fValue = new byte[12];
                if (!string.IsNullOrEmpty(PLCSNStr))
                {
                    MutexRegRW.WaitOne();
                    try
                    {
                        for (int i = 0; i < 6; i++)
                        {
                            fValue[i * 2 + 1] = (byte)(fRegisters[0x02 + i] & 0xFF);
                            fValue[i * 2] = (byte)((fRegisters[0x02 + i] >> 8) & 0xFF);
                        }
                    }
                    finally
                    {
                        MutexRegRW.ReleaseMutex();
                    }
                    return fValue;
                }
                return null;
            }
        }
    }

    /// <summary>
    /// 基于串口的总线管理器
    /// </summary>
    public class ZLPLCBusOnSerialPort : ZLErrorRecord
    {
        #region 成员

        public event SystemEventLog OnEventLog = null;

        #region 内部变量及属性

        protected NPNodesService fNPService { get; set; }
        /// <summary>
        /// 串口总线
        /// </summary>
        protected ZLSerialPortModbusToolBase fSerialPortModbus;
                
        public ZLSerialPortModbusToolBase SerialPortModbus
        {
            get { return fSerialPortModbus; }
        }
        /// <summary>
        /// 串口总线的配置
        /// </summary>
        protected ISerialPortBusCFG fPortBusCFG;

        public ISerialPortBusCFG PortBusCFG { get { return fPortBusCFG; } }
        /// <summary>
        /// 总线上的PLC节点
        /// </summary>
        protected Dictionary<int, ZLPLCModbusNode> fPLCNodeList = new Dictionary<int, ZLPLCModbusNode>();

        /// <summary>
        /// 线程
        /// </summary>
        protected Thread fSerialPortRunThd { get; set; }
        /// <summary>
        /// 模块线程是否运行标识，关闭程序时必须设为false
        /// </summary>
        protected bool ThdActive { get; set; }
        /// <summary>
        /// 暂停线程
        /// </summary>
        public bool ThdPause { get; set; }
        /// <summary>
        /// 线程锁字符串
        /// </summary>
        private const string fUniquKey = "ZLMBLib_2018_SerialPortBus_ZLPLCBusOnSerialPort";
        /// <summary>
        /// 线程锁
        /// </summary>
        private Mutex fThdMutexInstance { get; set; }

        /// <summary>
        /// 串口操作线程锁字符串
        /// </summary>
        private const string fUniquKeySerialPort = "ZLMBLib_2018_SerialPortBus_SerialPortOpterion";
        /// <summary>
        /// 串口操作线程锁
        /// </summary>
        private Mutex fThdMutexSerialPort { get; set; }
        /// <summary>
        /// 等待发送的命令列表
        /// </summary>
        List<TModbusRTUCommandV2> fTxCommandList = new List<TModbusRTUCommandV2>();
        #endregion

        #region 外部配置及属性
        /// <summary>
        /// 读取寄存器时间间隔
        /// </summary>
        protected int fSleepTick = 20;
        /// <summary>
        /// 读取寄存器时间间隔，单位：ms
        /// 最小时间间隔为10ms
        /// </summary>
        public int SleepTick
        {
            get { return fSleepTick; }
            set { fSleepTick = (value < 10) ? 10 : value; }
        }
        /// <summary>
        /// 应答超时
        /// </summary>
        protected int fResponTimeOut = 30;
        /// <summary>
        /// 应答超时，单位：ms
        /// 最小：5；
        /// 最大：300；
        /// </summary>
        public int ResponTimeOut
        {
            get { return fResponTimeOut; }
            set
            {
                if (value < 5)
                    fResponTimeOut = 5;
                else
                    if (value > 500)
                    fResponTimeOut = 300;
                else
                    fResponTimeOut = value;
            }
        }
        /// <summary>
        /// 丢失应答的错误最大次数
        /// </summary>
        protected int fErrorMaxCount = 5;
        /// <summary>
        /// 丢失应答的错误最大次数，最小3次；
        /// </summary>
        public int ErrorMaxCount
        {
            get { return fErrorMaxCount; }
            set { fErrorMaxCount = (value < 3) ? 3 : value; }
        }
        /// <summary>
        /// 通讯失败后，重新尝试连接延时
        /// </summary>
        protected int fRetryReadRegTick = 60;
        /// <summary>
        /// 通讯失败后，重新尝试连接延时，单位：s
        /// 最短30s
        /// </summary>
        public int RetryReadRegTick
        {
            get { return fRetryReadRegTick; }
            set { fRetryReadRegTick = (value < 30) ? 30 : value; }
        }

        /// <summary>
        /// 总线是否正常运行
        /// </summary>
        public bool Active
        { get { return (fSerialPortModbus != null) ? fSerialPortModbus.IsOpen && ThdActive : false; } }
        /// <summary>
        /// 总线的端口名称，如果未启动总线，名称为空
        /// </summary>
        public string PortName
        { get { return (fSerialPortModbus != null) ? fSerialPortModbus.PortName : ""; } }
        /// <summary>
        /// 当前总线描述信息，如果未启动总线，为空
        /// </summary>
        public string SerialPortInfo
        {
            get
            {
                string sSPortInf = "";
                if (fSerialPortModbus != null)
                {
                    sSPortInf = fSerialPortModbus.SerialPortInfo;
                    sSPortInf=string.Format(sSPortInf + ",PLCCount={0}({1}),ResponTimeOut={2}ms",
                        fPLCNodeList.Count().ToString(),
                        GetOnlinePLC().Length.ToString(),
                        fResponTimeOut.ToString());
                }
                return sSPortInf;
            }
        }
        /// <summary>
        /// 总线上PLC列表
        /// </summary>
        public int[] PLCAddressList { get { return fPLCNodeList.Keys.ToArray(); } }

        public string PipeName { get { return (fNPService != null) ? fNPService.SerPipeName : ""; } }
        #endregion

        #endregion

        #region 构建函数，清场函数，守护线程

        public ZLPLCBusOnSerialPort(ISerialPortBusCFG fPortBusCFGX)
        {
            // 总线配置
            fPortBusCFG = fPortBusCFGX;

            if (fPortBusCFG == null)
                fPortBusCFG = new SerialPortBusConfig(IniFile.NewIniFileName(),0);

            // 线程锁
            fThdMutexInstance = new Mutex(false, fUniquKey);
            fThdMutexSerialPort = new Mutex(false, fUniquKeySerialPort);
            // 创建并启动总线
            StarSerialPort(fPortBusCFG.AutoStar);

            // 启动命令管道
            fNPService = new NPNodesService();
            fNPService.OnRecCommand += ZLPLCBusOnSerialPort_OnRecCommand;
            fNPService.SerPipeName = fPortBusCFG.PipeName;
            fNPService.Active = true;
                        
            ThdPause = false;
            ThdActive = true;
            fSerialPortRunThd = new Thread(MainRunThdMethod);
            fSerialPortRunThd.Start();
        }
        /// <summary>
        /// 创建并启动总线
        /// </summary>
        /// <returns></returns>
        protected bool StarSerialPort(bool fStarOnCreate=true)
        {
            fSerialPortModbus = new ZLSerialPortModbusToolBase();
            Thread.Sleep(1);
            fSerialPortModbus.CRCEndian = fPortBusCFG.CRCEndian;
            fSerialPortModbus.RSConPORT = fPortBusCFG.RSConPORT;
            if (fStarOnCreate)
                fSerialPortModbus.OpenCOMPort(fPortBusCFG.PortName, fPortBusCFG.Baudrate);
            if (fSerialPortModbus.IsOpen)
                ReflashPLCRegList();
            return fSerialPortModbus.IsOpen;
        }

        private void ZLPLCBusOnSerialPort_OnRecCommand(object sender, ref NamePipeCommandBase fCommand)
        {
            if (fCommand != null)
            {
                byte fPLCAddr = (byte)fCommand.IndexSub;
                switch ((SerialPortNPCommand)fCommand.IndexMain)
                {
                    case SerialPortNPCommand.RestartBus:
                        fSerialPortModbus.CloseAll();
                        fSerialPortModbus = new ZLSerialPortModbusToolBase();
                        fSerialPortModbus.OpenCOMPort(fPortBusCFG.PortName, fPortBusCFG.Baudrate);
                        if (fSerialPortModbus.IsOpen)
                        {
                            fSerialPortModbus.CRCEndian = fPortBusCFG.CRCEndian;
                            fSerialPortModbus.RSConPORT = fPortBusCFG.RSConPORT;
                        }
                        fCommand = null;
                        break;
                    case SerialPortNPCommand.RestartPLC:
                        ReflashPLCRegList();
                        fCommand = null;
                        break;
                    case SerialPortNPCommand.GetBusInfo:
                        fCommand.PutDatas(SerialPortInfo);
                        break;
                    case SerialPortNPCommand.GetPLCData:
                        fPLCAddr = (byte)fCommand.IndexSub;
                        if (fPLCNodeList.ContainsKey(fPLCAddr))
                            fCommand.PutDatas(fPLCNodeList[fPLCAddr].RegValues);
                        break;
                    case SerialPortNPCommand.GetPLCAddrList:
                        int[] fPLCAddrList = fPLCNodeList.Keys.ToArray();
                        fCommand.PutDatas(fPLCAddrList);
                        break;
                    case SerialPortNPCommand.GetPLCInfo:
                        fPLCAddr = (byte)fCommand.IndexSub;
                        if (fPLCNodeList.ContainsKey(fPLCAddr))
                            fCommand.PutDatas(fPLCNodeList[fPLCAddr].PLCRunInfo);
                        break;
                    case SerialPortNPCommand.SetPLCData:
                        ushort[] fRegData = (ushort[])fCommand.GetDatas();
                        ushort fRegStar = (ushort)(fCommand.IndexSub & 0xFFFF);
                        fPLCAddr = (byte)((fCommand.IndexSub >> 16) & 0xFF);
                        int fTimeOut = this.ResponTimeOut;
                        if (fPLCNodeList.ContainsKey(fPLCAddr))
                            fCommand.PutDatas(WritePLCRegData(fPLCAddr, fRegStar, fRegData));
                        break;
                }
            }
        }
        /// <summary>
        /// 关闭总线，关闭线程，清理内存
        /// </summary>
        public void CloseBus()
        {
            ThdActive = false;
            fThdMutexSerialPort.WaitOne();
            try
            {
                fSerialPortModbus.CloseAll();
            }
            finally
            {
                fThdMutexSerialPort.ReleaseMutex();
            }
            // 等待线程将串口控件释放
            while (fSerialPortModbus != null)
                Thread.Sleep(1);
        }
        /// <summary>
        /// 停止总线，仅关闭串口，不释放总线
        /// </summary>
        public void StopBus()
        {
            fThdMutexSerialPort.WaitOne();
            try
            {
                fSerialPortModbus.Disconnect();
            }
            finally
            {
                fThdMutexSerialPort.ReleaseMutex();
            }
        }
        /// <summary>
        /// 启动总线，使用配置文件中的配置启动串口
        /// </summary>
        public void StarBus()
        {
            fSerialPortModbus.PortName = fPortBusCFG.PortName;
            fSerialPortModbus.BaudRate = fPortBusCFG.Baudrate;
            fSerialPortModbus.CRCEndian = fPortBusCFG.CRCEndian;
            fSerialPortModbus.RSConPORT = fPortBusCFG.RSConPORT;
            fSerialPortModbus.Connected = true;
            ReflashPLCRegList();
        }
        /// <summary>
        /// 启动总线，使用配置文件中的配置启动串口
        /// 增加：2019-10-19
        /// </summary>
        /// <param name="iResponTimeOut">命令发送后等待应答超时</param>
        public void StarBus(int iResponTimeOut)
        {
            fSerialPortModbus.PortName = fPortBusCFG.PortName;
            fSerialPortModbus.BaudRate = fPortBusCFG.Baudrate;
            fSerialPortModbus.CRCEndian = fPortBusCFG.CRCEndian;
            fSerialPortModbus.RSConPORT = fPortBusCFG.RSConPORT;
            fSerialPortModbus.Connected = true;
            ResponTimeOut = iResponTimeOut;
            ReflashPLCRegList();
        }

        private int fThdRunPos = 0;
        /// <summary>
        /// 主线程，不断的读取从PLC的数据
        /// </summary>
        /// <param name="parameter"></param>
        protected void MainRunThdMethod(object parameter)
        {
            fThdRunPos = 0;
            // 等待访问的PLC地址列表
            List<int> fAddressList = new List<int>();
            while (ThdActive)
                if (fSerialPortModbus != null)
                {
                    try
                    {
                        if (fSerialPortModbus.IsOpen && (!ThdPause))
                        {
                            if (fThdRunPos < fAddressList.Count)
                            {
                                try
                                {
                                    TModbusRTUCommandV2 fWriteCommand = null;
                                    foreach (int fPLCAddr in fAddressList)
                                        if (fPLCNodeList.ContainsKey(fPLCAddr))
                                        {
                                            ZLPLCModbusNode fPLCReg = fPLCNodeList[fPLCAddr];
                                            if (fPLCReg.WriteRegCommand != null)
                                            {
                                                fWriteCommand = fPLCReg.WriteRegCommand;
                                                fPLCReg.WriteRegCommand = null;
                                                break;
                                            }
                                        }
                                    if (fWriteCommand != null)
                                    {
                                        // 设定应答超时
                                        int fRTimeOut = fResponTimeOut;

                                        // 锁定线程的串口操作
                                        fThdMutexSerialPort.WaitOne();
                                        try
                                        {
                                            // 发送读取寄存器命令
                                            if (fSerialPortModbus.IsOpen)
                                                fSerialPortModbus.SendData(fWriteCommand, ref fRTimeOut);
                                        }
                                        finally
                                        {
                                            fThdMutexSerialPort.ReleaseMutex();
                                        }
                                    }
                                    else
                                    {
                                        // 提取PLC地址，并删除该记录    
                                        int fDestPLCAddress = fAddressList[fThdRunPos];
                                        // PLC节点列表中有相应记录，保证列表没有改变
                                        if (fPLCNodeList.ContainsKey(fDestPLCAddress))
                                        {
                                            ZLPLCModbusNode fPLCReg = fPLCNodeList[fDestPLCAddress];
                                            // 节点最后访问时间间隔
                                            TimeSpan fTPass = DateTime.Now - fPLCReg.LastComunication;
                                            // 判断是否需要读取PLC寄存器数据
                                            if (((fPLCReg.PLCDisconnectCount < fErrorMaxCount) ||    // 节点没有发生错误
                                                (fTPass.TotalSeconds > fRetryReadRegTick)) &&        // 节点已经有30秒未访问了
                                                 (fTPass.TotalMilliseconds > fPLCReg.IntervalOfFlash)
                                                 && (!ThdPause))
                                            {
                                                fPLCReg.LastComunication = DateTime.Now;
                                                // 设定应答超时
                                                int fRTimeOut = fResponTimeOut;
                                                fThdMutexSerialPort.WaitOne();
                                                try
                                                {
                                                    // 发送读取寄存器命令
                                                    if (fSerialPortModbus.IsOpen &&
                                                    (fSerialPortModbus.SendData(fPLCReg.MBCommand_ReadRegValue(),
                                                    ref fRTimeOut) <= 0))
                                                        // 没有收到正常的应答
                                                        fPLCReg.PLCLostRespon(1);
                                                }
                                                finally
                                                {
                                                    fThdMutexSerialPort.ReleaseMutex();
                                                }
                                            }
                                        }
                                    }
                                }
                                finally
                                {
                                    fThdRunPos++;
                                }
                            }
                            else
                            {
                                fThdRunPos = 0;
                                fAddressList.Clear();
                                // 线程锁，保证线程安全
                                fThdMutexInstance.WaitOne();
                                try
                                {
                                    // 创建等待访问的PLC节点列表
                                    fAddressList = new List<int>(fPLCNodeList.Keys);
                                }
                                finally
                                {
                                    fThdMutexInstance.ReleaseMutex();
                                }
                            }
                        }
                        Thread.Sleep(fSleepTick);
                    }
                    catch (Exception ex)
                    {
                        PutErrorEvent(ex);
                        if (OnEventLog != null)
                            OnEventLog(this, EventLogEntryType.Error, ex.Message);
                    }
                }
            fSerialPortModbus = null;
        }

        #endregion

        #region 外部功能函数
        /// <summary>
        /// 根据CFG配置文件中PLC列表的信息，重新创建总线上所有PLC节点
        /// </summary>
        /// <returns></returns>
        public int ReflashPLCRegList()
        {
            if (fPortBusCFG != null)
            {
                fThdMutexInstance.WaitOne();
                try
                {
                    fPLCNodeList.Clear();
                    string[] fPLCList = fPortBusCFG.PLCInfoList;
                    foreach (string fPLCInfo in fPLCList)
                    {
                        ZLPLCModbusNode fPLCRegs = new ZLPLCModbusNode(fPLCInfo);
                        if ((fPLCRegs.PLCMBAddress > 0) && (!fPLCNodeList.ContainsKey(fPLCRegs.PLCMBAddress)))
                            fPLCNodeList.Add(fPLCRegs.PLCMBAddress, fPLCRegs);
                    }
                }
                finally
                {
                    fThdMutexInstance.ReleaseMutex();
                }
            }
            return fPLCNodeList.Keys.Count;
        }
        /// <summary>
        /// 将当前PLC列表中的节点信息保存到CFG配置文件中
        /// </summary>
        /// <returns></returns>
        public int SaveAllPLCToCFG()
        {
            fPLCNodeList = fPLCNodeList.OrderBy(o => o.Value.PLCMBAddress).ToDictionary(p => p.Key, o => o.Value);
            List<string> fPLCInfoListTemp = new List<string>();
            if (fPLCNodeList.Count > 0)
                foreach (int fPLCAddr in fPLCNodeList.Keys)
                    fPLCInfoListTemp.Add(fPLCNodeList[fPLCAddr].CFGStr);
            fPortBusCFG.PLCInfoList = fPLCInfoListTemp.ToArray();
            return fPLCInfoListTemp.Count;
        }

        /// <summary>
        /// 清除所有节点
        /// </summary>
        public void ClearAllPLCNode()
        {
            fPLCNodeList.Clear();
            SaveAllPLCToCFG();
        }
        /// <summary>
        /// 增加新的PLC节点
        /// </summary>
        /// <param name="fPLCNode"></param>
        /// <returns>成功加入返回1；否则返回0</returns>
        public int AddNewPLCNode(ZLPLCModbusNode fPLCNode)
        {
            if ((fPLCNode != null)
                && (fPLCNode.PLCMBAddress > 0)
                && (!fPLCNodeList.ContainsKey(fPLCNode.PLCMBAddress)))
            {
                fThdMutexInstance.WaitOne();
                try
                {
                    fPLCNodeList.Add(fPLCNode.PLCMBAddress, fPLCNode);
                }
                finally
                {
                    fThdMutexInstance.ReleaseMutex();
                }
                SaveAllPLCToCFG();
                return 1;
            }
            return 0;            
        }
        public int AddNewPLCNode(int fPLCAddr, int fPLCRegStar, int fPLCRegCount)
        {
            return AddNewPLCNode(new ZLPLCModbusNode(fPLCAddr, fPLCRegStar, fPLCRegCount));
        }
        /// <summary>
        /// 删除指定节点
        /// </summary>
        /// <param name="fPLCAddress"></param>
        public int DelPLCNode(ZLPLCModbusNode fPLCNode)
        {
            if (fPLCNode != null)
                return DelPLCNode(fPLCNode.PLCMBAddress);
            return 0;
        }

        public int DelPLCNode(int fPLCAddr)
        {
            byte fPAddrTemp = (byte)fPLCAddr;
            if ((fPAddrTemp > 0) && (fPLCNodeList.ContainsKey(fPAddrTemp)))
            {
                fThdMutexInstance.WaitOne();
                try
                {
                    fPLCNodeList.Remove(fPAddrTemp);
                }
                finally
                {
                    fThdMutexInstance.ReleaseMutex();
                }
                SaveAllPLCToCFG();
                return 1;
            }
            return 0;
        }

        /// <summary>
        /// 获得指定地址的PLC节点
        /// </summary>
        /// <param name="fPLCAddress">目标PLC总线地址</param>
        /// <returns></returns>
        public ZLPLCModbusNode GetPLCNode(int fPLCAddress)
        {
            if (fPLCNodeList.ContainsKey(fPLCAddress))
                return fPLCNodeList[fPLCAddress];
            return null;
        }
        /// <summary>
        /// 获得在线PLC列表
        /// </summary>
        /// <returns></returns>
        public ZLPLCModbusNode[] GetOnlinePLC()
        {
            List<ZLPLCModbusNode> tResult = new List<ZLPLCModbusNode>();
            foreach (KeyValuePair<int, ZLPLCModbusNode> tVPLCNode in fPLCNodeList)
                if ((tVPLCNode.Value != null) && (tVPLCNode.Value.IsConnted))
                    tResult.Add(tVPLCNode.Value);
            return tResult.ToArray();
        }

        /// <summary>
        /// 将需要发送的命令加入到列表中，等待发送
        /// </summary>
        /// <param name="fTxCommand"></param>
        /// <returns></returns>
        public int SendModbusCommand(TModbusRTUCommandV2 fTxCommand, ref int fTxTimeOut)
        {
            int fResult = 0;
            if (fTxCommand != null)
            {
                ThdPause = true;
                try
                {
                    // 锁定线程操作
                    fThdMutexSerialPort.WaitOne();
                    try
                    {
                        fResult= fSerialPortModbus.SendData(fTxCommand, ref fTxTimeOut);
                        Thread.Sleep(10);
                    }
                    finally
                    {
                        fThdMutexSerialPort.ReleaseMutex();
                    }
                }
                finally
                {
                    ThdPause = false;
                }
            }
            return fResult;
        }

        /// <summary>
        /// 向PLC的寄存器写入数据
        /// </summary>
        /// <param name="fPLCAddr">目标PLC地址</param>
        /// <param name="fPLCRegStar">要写入的起始地址</param>
        /// <param name="fRegValues">要写入的数据</param>
        /// <returns>成功写入返回发送的数据量，否则返回-1</returns>
        public int WritePLCRegData(byte fPLCAddr, ushort fPLCRegStar, ushort[] fRegValues)
        {
            List<ushort> fRegList = new List<ushort>(fRegValues);
            ushort[] fRegVTemp = fRegList.ToArray();

            int fTOut = 0;
            if (fPLCNodeList.ContainsKey(fPLCAddr))
                return SendModbusCommand(fPLCNodeList[fPLCAddr].MBCommand_ReadWriteRegValue(fPLCRegStar, ref fRegVTemp), ref fTOut);
            else
                return -1;
        }
        
        public int SetResponeInterval(int iInterval)
        {
            fSerialPortModbus.CommandRunTimeOut = iInterval;
            return iInterval;
        }
        
        #endregion
    }
    
}
